//
// Created by Dell on 2024/8/13.
//
#include <jni.h>
#include <string>
#include <stdint.h>
#include <stdio.h>
#include <x264.h>
#include <android/log.h>

#define FAIL_IF_ERROR( cond, ... )\
do\
{\
    if( cond )\
    {\
        fprintf( stderr, __VA_ARGS__ );\
        goto fail;\
    }\
} while( 0 )


void my_x264_log(void *, int i_level, const char *psz_fmt, va_list arg)
{
    char *psz_prefix;
    android_LogPriority level;
    switch( i_level )
    {
        case X264_LOG_ERROR:
            psz_prefix = "error";
            level = ANDROID_LOG_ERROR;
            break;
        case X264_LOG_WARNING:
            psz_prefix = "warning";
            level = ANDROID_LOG_WARN;
            break;
        case X264_LOG_INFO:
            psz_prefix = "info";
            level = ANDROID_LOG_INFO;
            break;
        case X264_LOG_DEBUG:
            psz_prefix = "debug";
            level = ANDROID_LOG_DEBUG;
            break;
        default:
            psz_prefix = "unknown";
            level = ANDROID_LOG_UNKNOWN;
            break;
    }
//    __android_log_print(level, "x264", "x264 [%s]: ", psz_prefix);
    __android_log_vprint(level, "x264", psz_fmt, arg);
}


extern "C"
JNIEXPORT jint JNICALL
Java_com_dan_x264_X264_test(JNIEnv *env, jclass clazz) {

    int width, height;
    x264_param_t param;
    x264_picture_t pic;
    x264_picture_t pic_out;
    x264_t *h;
    int i_frame = 0;
    int i_frame_size;
    x264_nal_t *nal;
    int i_nal;

    const char * in_filename = "/sdcard/x264/blue_sky_1080p25.y4m";
    width = 1920;
    height = 1080;
    FILE *in = fopen(in_filename, "rb");

    const char * out_filename = "/sdcard/x264/blue_sky_1080p25.y4m.h264";
    FILE *out = fopen(out_filename, "wb+");

#ifdef _WIN32
    _setmode( _fileno( stdin ),  _O_BINARY );
    _setmode( _fileno( stdout ), _O_BINARY );
    _setmode( _fileno( stderr ), _O_BINARY );
#endif

//    FAIL_IF_ERROR( !(argc > 1), "Example usage: example 352x288 <input.yuv >output.h264\n" );
//    FAIL_IF_ERROR( 2 != sscanf( argv[1], "%dx%d", &width, &height ), "resolution not specified or incorrect\n" );

    /* Get default params for preset/tuning */
    if( x264_param_default_preset( &param, "ultrafast", NULL ) < 0 )
        return -1;

    /* Configure non-default params */
    param.i_bitdepth = 8;
    param.i_csp = X264_CSP_I420;
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0;
    param.b_repeat_headers = 1;
    param.b_annexb = 1;

    // 自己添加的额外测试参数
    param.b_opencl = 1;
    param.i_log_level = X264_LOG_DEBUG;
    param.pf_log = my_x264_log;
    param.analyse.b_psnr = 1;
    param.analyse.b_ssim = 1;
    param.analyse.b_mb_info = 1;
    param.analyse.b_mb_info_update = 1;


    /* Apply profile restrictions. */
    if( x264_param_apply_profile( &param, "baseline" ) < 0 )
        return -2;

    if( x264_picture_alloc( &pic, param.i_csp, param.i_width, param.i_height ) < 0 )
        return -3;
#undef fail
#define fail fail2

    h = x264_encoder_open( &param );
    if( !h )
        return -4;
#undef fail
#define fail fail3

    int luma_size = width * height;
    int chroma_size = luma_size / 4;
    /* Encode frames */
    for( ;; i_frame++ )
    {
        /* Read input frame */
        if( fread( pic.img.plane[0], 1, luma_size, in ) != (unsigned)luma_size )
            break;
        if( fread( pic.img.plane[1], 1, chroma_size, in ) != (unsigned)chroma_size )
            break;
        if( fread( pic.img.plane[2], 1, chroma_size, in ) != (unsigned)chroma_size )
            break;

        pic.i_pts = i_frame;
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
        if( i_frame_size < 0 )
            return -5;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, out ) )
                return -6;
        }
    }
    /* Flush delayed frames */
    while( x264_encoder_delayed_frames( h ) )
    {
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out );
        if( i_frame_size < 0 )
            return -7;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, out ) )
                return -8;
        }
    }

    x264_encoder_close( h );
    x264_picture_clean( &pic );
    return 0;

#undef fail
    fail3:
    x264_encoder_close( h );
    fail2:
    x264_picture_clean( &pic );
    fail:
    return -1;

}